
/****************************************************************************
**
** Copyright (C) 2004-2008 VATATA.com . All rights reserved.
**
** This file is the part of the Miniutil Poject of VATATA.com.
** Vatata's miniutil library is lightweight and portable (for linux
** and Windows). The C++ Class Library included Threadpool, Dll danymic loading, 
** General socket application model and tcp server, httpserver/servlet, etc. 
** It is a pratical utility disigned for who don't want learn the Big C++ 
** Developing Platform, such as  Boost or ACE.
**
** This file may be used under the terms of the GNU General Public
** License versions 2.0 or 3.0 as published by the Free Software
** Foundation and appearing in the files LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file.  Alternatively you may (at
** your option) use any later version of the GNU General Public
** License if such license has been publicly approved by VATATA.com.
** (or its successors, if any) and the Miniutil Project Aministrators. 
**
** Please review the following information to ensure GNU General
** Public Licensing requirements will be met:
** http://www.gnu.org/licenses/lgpl.html. If you are unsure which 
** license is appropriate for your use, please review the following 
** information: http://code.google.com/p/miniutil/ or contact 
** http://www.vatata.com.
**
** In addition, as a special exception, VATATA.com, as the sole
** copyright holder for Miniutil Project, grants users of VATATA
** P2P Platform the right to release and package with the related 
** libraries without the source code.
**
** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE. vatata reserves all rights not expressly
** granted herein.
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
****************************************************************************/



#include "miniutil/buffer.h"

using namespace miniutil::data;

void buffer::set_buffer_alloc(buffer_alloc* p)
{
	p_buf_alloc = p;
}

buffer_alloc* buffer::p_buf_alloc = NULL;


//-----------------------------------------------
buffer_error::buffer_error( const std::string & msg ) : std::runtime_error(msg)
{
	M_ERROR( "miniutil::data::buff_error: %s\n", msg.c_str() );
}

int buffer::counter = 0;

buffer::buffer(size_t buffer_size) : _size(buffer_size), _offset(0), _length(0)
{
	counter ++;

	if(p_buf_alloc)
		_data = p_buf_alloc->retrieve_mem(buffer_size);
	else
		_data = (char *) malloc(buffer_size);
}

buffer::buffer( const buffer & b) : _size( b._length ), _offset( 0 ), _length(b._length)
{
	counter ++;
	if(p_buf_alloc)
		_data = p_buf_alloc->retrieve_mem(_size);
	else
		_data = (char *) malloc( _size );
	memcpy( _data, b._data + b._offset,  _length );
}

buffer::buffer( const char * pdata, size_t len ) : _size( len ), _offset(0), _length(len)
{
	counter++;

	if(p_buf_alloc)
		_data = p_buf_alloc->retrieve_mem(_size);
	else
		_data = (char *)malloc( _size );
	memcpy( _data, pdata, len );
}

buffer::~buffer()
{
	counter--;
	if(p_buf_alloc)
		p_buf_alloc->release_mem(_data, _size);
	else
		free( _data );
}

void buffer::fill( int c )
{
	memset( _data, c, _size );
}
size_t buffer::size()
{
	return _size;
}
size_t buffer::length()
{
	return _length;
}

size_t buffer::enlarge( size_t inc_len )
{
    size_t free_len = _size - _length - _offset;
	if( free_len > inc_len )
        return free_len;
    if ( _size - _length > inc_len )
    {
        this->resize( _size );
        return _size - _length;
    }
    this->resize(  _offset + _length + inc_len );
    return _size - _length - _offset;
}

size_t buffer::free_length()
{
    if ( _offset > _size / 3 )
        this->resize( _size );
	return _size - _length - _offset;
}

void buffer::set_length(size_t newlength)
{
	if ( newlength > _size )
		this->resize( newlength );
	_length = newlength;
}

void buffer::inc_length(size_t inc_len)
{
	this->set_length( inc_len + _length );
}

/*
	fflag:
		true    change the buffer the newsize not considering current data length
		false	if current size larger than newsize, not change
				if current size small than length, change to length
*/
buffer & buffer::resize( size_t newsize, bool fflag)
{

	if(newsize == _size ||(newsize < _size && fflag == false))
	{
		memmove( _data,  _data+_offset, _length );
		_offset = 0;
	}
	else
	{
		int allocasize = 0;
		if(newsize > _length || fflag == true)
		{
			allocasize = (( (newsize-1) >> 10 ) + 1) << 10 ;
		}
		else
		{
			allocasize = (( (_length-1) >> 10 ) + 1) << 10 ;
		}

		char * newdata = NULL;
		if(p_buf_alloc)
			newdata = p_buf_alloc->retrieve_mem(allocasize);
		else
			newdata = (char*)malloc(allocasize);

		if(_length > 0)
		{
			if(allocasize > _length )
				memcpy( newdata, _data + _offset, _length);
			else
			{
				memcpy( newdata, _data + _offset, allocasize);
				_length = allocasize;
			}
		}
		

		if(p_buf_alloc)
			p_buf_alloc->release_mem(_data, _size);
		else
			free( _data );

		_data = newdata;
		_size = allocasize;
		_offset = 0;
	}

    assert(_size >= _length);
	return *this;
}

buffer& buffer::diet()
{
	if ( _length != _size )
	{
		char * newdata = NULL;
		
		if(p_buf_alloc)
			newdata = p_buf_alloc->retrieve_mem(_length);
		else
			newdata = (char *) malloc(_length);

		memcpy( newdata, _data + _offset, _length );

		if(p_buf_alloc)
			p_buf_alloc->release_mem(_data, _size);
		else
			free( _data );

		_data = newdata;
		_size = _length;
		_offset = 0;
	}
    assert(_size >= _length);
	return *this;
}


buffer & buffer::append(const char * pdata, size_t len )
{
	if ( _size - _offset - _length >= len )
	{
		memcpy( _data + _offset + _length, pdata, len );
		_length += len;
	}
	else
	{
		resize( _length + len );
		memcpy( _data + _length, pdata, len );
		_length += len;
	}
    assert(_size >= _length);
	return * this;
}

buffer & buffer::trunc( size_t trunc_count )
{
	if ( trunc_count >= _length )
	{
		_length = 0;
		_offset = 0;
	}
	else
		_length -= trunc_count;

    assert(_size >= _length);
	return *this;
}

buffer& buffer::erase( size_t off, size_t len )
{
	if ( len >= _length )
	{
		_length = 0;
		_offset = 0;
		return * this;
	}

	if ( off == 0 )
	{
		_offset += len;
		_length -= len;
	}
	else
	{
		memmove( _data + _offset + off, _data + _offset + off + len, _length - off - len );
		_length -= len;
	}

    assert(_size >= _length);
	return * this;
}

buffer& buffer::shift( size_t len )
{
	if ( len >= _length )
	{
		_length = 0;
		_offset = 0;
	}
	else
	{
		_offset += len;
		_length -= len;
	}
    assert(_size >= _length);
	return * this;
}

buffer& buffer::clear()
{
		_length = 0;
		_offset = 0;
    assert(_size >= _length);
	return * this;
}


buffer& buffer::insert( size_t off, const char * pdata, size_t len )
{
	if ( off >= _length )
	{
		return append( pdata, len );
	}

	if ( _offset >= len )
	{
		if ( off )
			memmove( _data + _offset - len, _data + _offset, off );
		memcpy( _data + _offset -len + off, pdata,  len );
		_offset -= len;
		_length += len;
	    assert(_size >= _length);
	}
	else if ( _size - _offset - _length >= len )
	{
		memmove( _data + _offset + off + len , _data + _offset + off , _length - off );
		memcpy( _data + _offset + off, pdata, len );
		_length += len;
	    assert(_size >= _length);
	}
	else
	{
		size_t newsize = (( (len + _length) >> 10 ) + 1) << 10 ;
		char * newdata = (char *) malloc( newsize);
		memcpy( newdata, _data + _offset, off );
		memcpy( newdata+off, pdata, len );
		memcpy( newdata+off+len, _data+ _offset + off, _length - off );
		_offset = 0;
		_length += len;
		_size = newsize;
		free( _data );
		_data = newdata;
	    assert(_size >= _length);
	}
	return *this;
}

buffer& buffer::unshift(const char * pdata, size_t len )
{
	return insert( 0, pdata, len );
}

buffer & buffer::append( const buffer & b )
{
	return append( b._data + b._offset, b._length );
}

buffer buffer::sub( size_t off, size_t len)
{
	if ( off + len > _length )
		len = _length - off;
	return buffer( _data + _offset + off, len  );
}

size_t buffer::copyTo( char * out , size_t off, size_t len )
{
	if ( off + len > _length )
		len = _length - off;
	memcpy( out, _data + _offset + off, len );

	return len;
}


buffer & buffer::operator=( const buffer &b)
{
    if(this == &b)
        return (*this);

    if(p_buf_alloc)
		p_buf_alloc->release_mem(_data, _size);
	else
		free( _data );

	_size = b._size;

	if(p_buf_alloc)
		_data = p_buf_alloc->retrieve_mem(_size);
	else
		_data = (char *)malloc( _size );

    memcpy( _data, b._data, _size );
    _length = b._length;
    _offset = 0;

	return *this;
}

char * buffer::ptr( size_t index )
{
	return _data + index;
}

char & buffer::operator[](size_t index)
{
	if ( index + _offset >= _size )
		throw buffer_error("index out of buffer's size!");
	return _data[index + _offset ];
}

char & buffer::at(size_t index)
{
	if ( index + _offset >= _size )
		throw buffer_error("index out of buffer's size!");
	return _data[index + _offset ];
}


char * buffer::data( size_t index )
{
	return _data + _offset + index;
}


char * buffer::tail(size_t index)
{
	return _data + _offset + _length - index;
}

char * buffer::c_str( size_t index )
{
	if ( _offset + _length >= _size )
		this->resize( _offset + _length + 1 );

	*( _data + _offset + _length ) = 0;
	return _data + _offset + index;
}

void buffer::dump()
{
	if ( MCHECK_DEBUG ) 
	{
		M_DEBUG("---------------buffer::dump(): size: %d length: %d", this->size(), this->length() ); 
		for( size_t i=0; i< length(); i++ )	
		{
			if ( i % 20 == 0 ) M_DEBUG("\n" );
			M_DEBUG(" %2x" ,( 0xff) & at(i) ); 
		}
		M_DEBUG("\nEnd buffer::dump()----------------------------------\n " );
	}
}

shared_buffer buffer::share()
{
	return ref_ptr<buffer>( new buffer(*this) );
}

shared_buffer buffer::share(size_t off, size_t len )
{
	if ( off + len > _length )
		len = _length - off;
	return shared_buffer( new buffer( _data + _offset + off, len ) );
}

shared_buffer buffer::newInstance( const buffer & b )
{
	return shared_buffer( new buffer(b) );
}
shared_buffer buffer::newInstance( size_t buffer_size )
{
	return shared_buffer( new buffer(buffer_size) );
}
shared_buffer buffer::newInstance( char * data, size_t len )
{
	return shared_buffer( new buffer(data, len ) );
}
